package com.zon.len.dynamic;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.zon.len.dynamic.exception.DynamicDataSourceException;
import com.zon.len.dynamic.properties.DruidConfigProperties;
import com.zon.len.dynamic.properties.DruidConfigProperties.DruidSlf4jConfig;
import com.zon.len.dynamic.utils.DruidWallConfigUtil;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;

/**
 * @author ZonLen since on 2021/12/31 下午5:33
 */
public class DruidDataSourceCreator  {

  private final ApplicationContext applicationContext;

  public DruidDataSourceCreator(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
  }

  public DruidDataSource createDataSource(DruidConfigProperties dataSourceProperty) {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUsername(dataSourceProperty.getUsername());
    dataSource.setPassword(dataSourceProperty.getPassword());
    dataSource.setUrl(dataSourceProperty.getUrl());
    dataSource.setDriverClassName(dataSourceProperty.getDriverClassName());
    dataSource.setName(dataSourceProperty.getSourceKey());
    Properties properties = dataSourceProperty.toProperties(dataSourceProperty);
    String filters = properties.getProperty("druid.filters");
    List<Filter> proxyFilters = new ArrayList<>(2);
    if (!StringUtils.isEmpty(filters) && filters.contains("stat")) {
      StatFilter statFilter = new StatFilter();
      statFilter.configFromProperties(properties);
      proxyFilters.add(statFilter);
    }
    if (!StringUtils.isEmpty(filters) && filters.contains("wall")) {
      WallConfig wallConfig = DruidWallConfigUtil
          .toWallConfig(dataSourceProperty.getWall(), dataSourceProperty.getWall());
      WallFilter wallFilter = new WallFilter();
      wallFilter.setConfig(wallConfig);
      proxyFilters.add(wallFilter);
    }
    if (!StringUtils.isEmpty(filters) && filters.contains("slf4j")) {
      Slf4jLogFilter slf4jLogFilter = new Slf4jLogFilter();
      // 由于properties上面被用了，LogFilter不能使用configFromProperties方法，这里只能一个个set了。
      DruidSlf4jConfig slf4jConfig = dataSourceProperty.getSlf4j();
      slf4jLogFilter.setStatementLogEnabled(slf4jConfig.getEnable());
      slf4jLogFilter
          .setStatementExecutableSqlLogEnable(slf4jConfig.getStatementExecutableSqlLogEnable());
      proxyFilters.add(slf4jLogFilter);
    }

    if (this.applicationContext != null) {
      for (String filterId : dataSourceProperty.getProxyFilters()) {
        proxyFilters.add(this.applicationContext.getBean(filterId, Filter.class));
      }
    }
    dataSource.setProxyFilters(proxyFilters);
    dataSource.configFromPropety(properties);
    //连接参数单独设置
    dataSource.setConnectProperties(dataSourceProperty.getConnectionProperties());
    //设置druid内置properties不支持的的参数
    Boolean testOnReturn =
        dataSourceProperty.getTestOnReturn() == null ? dataSourceProperty.getTestOnReturn()
            : dataSourceProperty
                .getTestOnReturn();
    if (testOnReturn != null && testOnReturn.equals(true)) {
      dataSource.setTestOnReturn(true);
    }
    Integer validationQueryTimeout =
        dataSourceProperty.getValidationQueryTimeout() == null ? dataSourceProperty
            .getValidationQueryTimeout()
            : dataSourceProperty.getValidationQueryTimeout();
    if (validationQueryTimeout != null && !validationQueryTimeout.equals(-1)) {
      dataSource.setValidationQueryTimeout(validationQueryTimeout);
    }

    Boolean sharePreparedStatements =
        dataSourceProperty.getSharePreparedStatements() == null ? dataSourceProperty
            .getSharePreparedStatements()
            : dataSourceProperty.getSharePreparedStatements();
    if (sharePreparedStatements != null && sharePreparedStatements.equals(true)) {
      dataSource.setSharePreparedStatements(true);
    }
    Integer connectionErrorRetryAttempts =
        dataSourceProperty.getConnectionErrorRetryAttempts() == null ? dataSourceProperty
            .getConnectionErrorRetryAttempts()
            : dataSourceProperty.getConnectionErrorRetryAttempts();
    if (connectionErrorRetryAttempts != null && !connectionErrorRetryAttempts.equals(1)) {
      dataSource.setConnectionErrorRetryAttempts(connectionErrorRetryAttempts);
    }
    Boolean breakAfterAcquireFailure =
        dataSourceProperty.getBreakAfterAcquireFailure() == null ? dataSourceProperty
            .getBreakAfterAcquireFailure()
            : dataSourceProperty.getBreakAfterAcquireFailure();
    if (breakAfterAcquireFailure != null && breakAfterAcquireFailure.equals(true)) {
      dataSource.setBreakAfterAcquireFailure(true);
    }

    Integer timeout =
        dataSourceProperty.getRemoveAbandonedTimeoutMillis() == null ? dataSourceProperty
            .getRemoveAbandonedTimeoutMillis()
            : dataSourceProperty.getRemoveAbandonedTimeoutMillis();
    if (timeout != null) {
      dataSource.setRemoveAbandonedTimeout(timeout);
    }

    Boolean abandoned =
        dataSourceProperty.getRemoveAbandoned() == null ? dataSourceProperty.getRemoveAbandoned()
            : dataSourceProperty.getRemoveAbandoned();
    if (abandoned != null) {
      dataSource.setRemoveAbandoned(abandoned);
    }

    Boolean logAbandoned =
        dataSourceProperty.getLogAbandoned() == null ? dataSourceProperty.getLogAbandoned()
            : dataSourceProperty
                .getLogAbandoned();
    if (logAbandoned != null) {
      dataSource.setLogAbandoned(logAbandoned);
    }

    Integer queryTimeOut =
        dataSourceProperty.getQueryTimeout() == null ? dataSourceProperty.getQueryTimeout()
            : dataSourceProperty
                .getQueryTimeout();
    if (queryTimeOut != null) {
      dataSource.setQueryTimeout(queryTimeOut);
    }

    Integer transactionQueryTimeout =
        dataSourceProperty.getTransactionQueryTimeout() == null ? dataSourceProperty
            .getTransactionQueryTimeout()
            : dataSourceProperty.getTransactionQueryTimeout();
    if (transactionQueryTimeout != null) {
      dataSource.setTransactionQueryTimeout(transactionQueryTimeout);
    }

    try {
      dataSource.init();
    } catch (SQLException e) {
      throw new DynamicDataSourceException(
          "Dynamic datasource [" + dataSourceProperty.getSourceKey()
              + "] init fail, Please check datasource config", e);
    }
    return dataSource;
  }

}
